<!--

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.

-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>Preferences API in NetBeans</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <h1 align="left">Preferences API in NetBeans</h1>
    NetBeans adopts <a href="https://docs.oracle.com/javase/1.5.0/docs/guide/preferences/index.html">Java Preferences API</a>
    standard to be used in NetBeans to
    store preference and configuration data to be able to adapt to the needs
    of different users. NetBeans keeps this standard and enhances it slightly
    by providing its own preference tree in addition to default system and user
    roots. The purpose for this enhancement is to
    unify the way how persistent preference and configuration data are stored in NetBeans
    whereas all changes go into userdir which is the central place where also
    other configuration data are kept.This concept is convenient for developers
    or power users for running more instances of NetBeans, for debugging,
    for testing, for analysing structure or content of such files whenever any
    problems occures whereas running NetBeans with various userdirs means
    actually running NetBeans with different preferences. Running NetBeans with
    fresh userdir means actually running NetBeans with factory preset
    defaults.<P>
    The decision whether to use standard or  enhanced NetBeans preference tree is left
    up to the developer.
    <h3 align="left">Usage Notes</h3>
    <h4 align="left">How to obtain NetBeans Preferences?</h4>
    To get preference node there are two <A
      HREF="@org-openide-util@/org/openide/util/NbPreferences.html">factory
    methods in Utilities API (org.openide.util)</A>:
    <ul>
      <li><code>NbPreferences.root()</code> returns root preference node. </li>
      <li><code>NbPreferences.forModule(class cls)</code> returns
      preference node which <a
        href="@JDK@/java/util/prefs/Preferences.html#absolutePath%28%29">absolute path</a> 
      depends whether class provided as a parameter was loaded as a
      part of any module or not. If so, then absolute path corresponds to
      slashified code name base of module. If not, then absolute path
      corresponds to class's package </li>
    </ul>
    <h4 align="left">How to verify that preferences work properly?</h4>
    Layout of files on disk:<br>
    <p> </p>
    <pre>
    config/Preferences/
    `-- org
        |-- apache
        |   `-- tools
        |       `-- ant
        |           `-- module.properties
        `-- netbeans
            `-- modules
                `-- derby.properties
    </pre>

    <p> prefs.properties is regular properties file. See: </p>
    <p> </p>
<pre>#Thu Sep 28 18:15:22 CEST 2006<br>location=/tmp/location<br>systemHome=/tmp/systemHome<br>  </pre>
    <H4 ALIGN=LEFT>How to migrate from SystemOptions into Preferences API?</H4>
    <OL>
      <LI>Remove registration from layer: 
<PRE>
-     &lt;folder name="Services"/&gt;
-         &lt;file name="org-netbeans-modules-derby-DerbyOptions.settings" url="DerbyOptions.settings"/&gt;
-     &lt;/folder&gt;
</PRE>
      <LI>Add module dependency on Utilities API (org.openide.util with specification
      version &gt;= 7.4) if necessary
      <LI>Remove module dependency on Settings Options API (org.openide.options)
      <LI>Remove registration of Node which was used to visualize original
      SystemOptions in Tools/Options
      <PRE>
-     &lt;folder name="Services"&gt;
-         &lt;folder name="IDEConfiguration"&gt;
-             &lt;folder name="ServerAndExternalToolSettings"&gt;
-                    &lt;file name="org-netbeans-modules-derby-DerbyOptions.shadow"&gt;
-                        &lt;attr name="originalFile" stringvalue="Services/org-netbeans-modules-derby-DerbyOptions.settings"/&gt;
-                    &lt;/file&gt;
-             &lt;/folder&gt;
-         &lt;/folder&gt;
-     &lt;/folder&gt;

      </PRE>
      <LI>Rewrite the code not to extend SystemOption anymore
      <LI>Ensure visualization in Tools/Options if still needed.
      <LI>Ensure import from previous version into preferences storage
      <LI>Delete associated BeanInfo if it isn't necessary anymore
      <LI>Document usage of Preferences API like any other API in arch document
      <LI>Run tests to verify
      that code after migration is working
    </OL>
    <H4 ALIGN=LEFT>Hints how to replace SystemOption </H4>
    It is recommended to delete the whole settings class extending SystemOption
    and call directly Preferences API. This approach isn't convenient when
    except reading, writing from storage is necessary additional logic. Then
    following steps can be used as a hint:  
    <OL>
      <LI>Change your setting class not to extend SystemOption anymore
      <LI>Either use static methods for getters and setters or replace factory method SharedClassObject.findObject by common singleton
      pattern:
      <PRE>
-public class DerbyOptions extends SystemOption {
-
-    private static final long serialVersionUID = 1101894610105398924L;
+public class DerbyOptions  {
+    private static DerbyOptions INSTANCE = new DerbyOptions();

     public static DerbyOptions getDefault() {
-        return (DerbyOptions)SharedClassObject.findObject(DerbyOptions.class, true);
+        return INSTANCE;
     }
      </PRE>
      <LI>Replace all other SystemOption and SharedClassObject calls. E.g.:
      <PRE>
+    protected final String putProperty(String key, String value, boolean notify) {
+        String retval = getPreferences().get(key, null);
+        if (value != null) {
+            NbPreferences.forModule(DerbyOptions.class).put(key, value);
+        } else {
+            NbPreferences.forModule(DerbyOptions.class).remove(key);
+        }
+        return retval;
+    }

+    protected final String getProperty(String key) {
+        return NbPreferences.forModule(DerbyOptions.class).get(key, null);
+    }
      </PRE>
    </OL>
    <H4 ALIGN=LEFT>How to ensure visualization in Tools/Options?</H4>
    If you need keep UI backward compatibility then the way is very easy:
    provide a node representing your settings class. See:
    <PRE>

+    public static BeanNode createViewNode() throws IntrospectionException {
+        return new BeanNode(DerbyOptions.getDefault());
+    }

-    &lt;file name="org-netbeans-modules-derby-DerbyOptions.shadow"&gt;
-        &lt;attr name="originalFile" stringvalue="Services/org-netbeans-modules-derby-DerbyOptions.settings"/&gt;
+    &lt;file name="DerbyOptionsNode.instance"&gt;
+        &lt;attr name="instanceCreate" methodvalue="org.netbeans.modules.derby.DerbyOptions.createViewNode"/&gt;

    </PRE>
    The other way means to go a little more farther and provide and install custom
    options panels/categories to Options Dialog according to the
    <A HREF="@org-netbeans-modules-options-api@/overview-summary.html">Options Dialog API</A>.
    The module development support in NetBeans can help by generating boilerplate code and
    registering the option into your layer.
    <H4 ALIGN=LEFT>How to document usage of Preferences API?</H4>
    Answer properly arch questions like any other <A
      HREF="https://netbeans.apache.org/wiki/API_Design">APIs</A>. There was added new arch question related to
    preferences (resources-preferences). See example:
    <PRE>
&lt;answer id="resources-preferences"&gt;
  &lt;api group="preferences" name="org.netbeans.modules.derby" type="export" category="private" url=""&gt;
      &lt;table&gt;
          &lt;tbody&gt;
              &lt;tr&gt;
                  &lt;th&gt;key&lt;/th&gt;
                  &lt;th&gt;description&lt;/th&gt;
                  &lt;th&gt;read&lt;/th&gt;
                  &lt;th&gt;write&lt;/th&gt;
              &lt;/tr&gt;
              &lt;tr&gt;
                  &lt;td&gt;location&lt;/td&gt;
                  &lt;td&gt;Derby location or an empty string if the Derby location is not set&lt;/td&gt;
                  &lt;td&gt;x&lt;/td&gt;
                  &lt;td&gt;x&lt;/td&gt;
              &lt;/tr&gt;
              &lt;tr&gt;
                  &lt;td&gt;systemHome&lt;/td&gt;
                  &lt;td&gt;Derby system home or an empty string if the system home is not set&lt;/td&gt;
                  &lt;td&gt;x&lt;/td&gt;
                  &lt;td&gt;x&lt;/td&gt;
              &lt;/tr&gt;
          &lt;/tbody&gt;
      &lt;/table&gt;
  &lt;/api&gt;
 &lt;/answer&gt;
    </PRE>
    <H4 ALIGN=LEFT>How to ensure import from previous version?</H4>
There is support for importing old serialized instances of SystemOptions into
preferences storage in ide/launcher/upgrade module in package
org.netbeans.upgrade.systemoptions. This infrastructure reads configuration file
containing enumeration of settings files that should be imported. In simple
cases there is actually no neeed to code anything, but this is rare case which
may happen just when all the properties are primitive data types or String and
if method writeExternal isn't overridden.<P>
How to start? The best is to start with tests. But first you must have settings
file that will be parsed and tested. Put this file among tests:
<PRE>
test/unit/src/org/netbeans/upgrade/systemoptions
|-- BasicTestForImport.java
|-- DerbyOptionsTest.java
|-- org-netbeans-modules-derby-DerbyOptions.settings
</PRE>
Then write simple test that should contain assertions what actually will be
parsed and imported. See:
<PRE>
public class DerbyOptionsTest extends BasicTestForImport {
    public DerbyOptionsTest(String testName) {
        super(testName, "org-netbeans-modules-derby-DerbyOptions.settings");
    }    
    public void testPreferencesNodePath() throws Exception {
        assertPreferencesNodePath("/org/netbeans/modules/derby");
    }    
    public void testPropertyNames() throws Exception {
        assertPropertyNames(new String[] {
            "systemHome",
            "location"
        });
    }    
    public void testSystemHome() throws Exception {
        assertPropertyType("systemHome","java.lang.String");
        assertProperty("systemHome","/tmp/systemHome");
    }
    public void testLocation() throws Exception {
        assertPropertyType("location","java.lang.String");
        assertProperty("location","/tmp/location");
    }    
}
</PRE>
If the tests  didn't pass then probably there is more complicated object graph
serialized then you must subclass <CODE>PropertyProcessor</CODE> and put your
own code in.
(See as an example: <A HREF="https://github.com/apache/netbeans/tree/master/nb/o.n.upgrader/src/org/netbeans/upgrade/systemoptions/TaskTagsProcessor.java">TaskTagsProcessor</A>
 ).
 <H4 ALIGN=LEFT>How to write tests that needs Preferences API?</H4>
 Not persistent implementation of <code>java.util.prefs.Preferences</code> 
 is installed in place of the platform-specific default implementation for
 running tests which is in spirit of unit testing because individual tests
 shouldn't interact. But if this behaviour isn't suitable then there is possible
 to reinstall platform-specific default implementation again:  
<PRE>
+         public void run(final TestResult result) {
+             //just initialize Preferences before code NbTestCase
+             Preferences.userRoot();                        
+             super.run(result);
+         }
</PRE>
</body>
</html>
